package pl.skubicha.controller;

import javax.validation.Valid;

import org.apache.commons.lang.StringUtils;
import org.jboss.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import pl.skubicha.domain.Groups;
import pl.skubicha.exceptions.EntityConstraintException;
import pl.skubicha.exceptions.GroupNotFoundException;
import pl.skubicha.response.GroupDTO;
import pl.skubicha.response.SearchDTO;
import pl.skubicha.service.GroupsService;
import pl.skubicha.utils.GroupMapper;
import pl.skubicha.utils.Utils;

@Controller
@RequestMapping( { "/groups" } )
@PreAuthorize("hasRole('Administrator')")
public class GroupsController {

	private static final String GROUP_NAME = "groupName";
	private static final String NUMERIC_GROUP_NAME = "numericGroupName";

	private static final int GROUPS_PER_PAGE = 4;
	private static final int DEFAULT_PAGE = 1;

	private static Logger logger = Logger.getLogger( GroupsController.class );

	@Autowired
	private GroupsService service;

	@RequestMapping( { "", "/" } )
	public String defaultPage( final ModelMap model, @RequestParam( required = false ) final String order, @RequestParam( required = false ) final String column,
			@RequestParam( required = false ) final String searchString ) {
		getPageWithNumber( DEFAULT_PAGE, model, order, column, searchString );
		return "groups/list";
	}

	@RequestMapping( "/{pageNumber}" )
	public String getPageWithNumber( @PathVariable final Integer pageNumber, final ModelMap model, @RequestParam( required = false ) final String order,
			@RequestParam( required = false ) final String column, @RequestParam( required = false ) final String searchString ) {
		getPage( pageNumber, model, order, column, searchString );
		return "groups/list";
	}

	private void getPage( final Integer pageNumber, final ModelMap model, String order, final String column, final String searchString ) {
		final Page< Groups > groupsPage = getGroupsPerPage( searchString, new PageRequest( pageNumber - 1, GROUPS_PER_PAGE, Utils.getSortingDirection( order ),
				getSortedColumn( column ) ) );
		final int currentPage = groupsPage.getNumber() + 1;
		final int beginPage = Math.max( 1, currentPage - 5 );
		final int endPage = Math.min( beginPage + 10, ( groupsPage.getTotalPages() == 0 ? 1 : groupsPage.getTotalPages() ) );
		final int totalPages = ( groupsPage.getTotalPages() == 0 ? 1 : groupsPage.getTotalPages() );

		model.addAttribute( "groupsPage", groupsPage );
		model.addAttribute( "totalRecords", groupsPage.getTotalElements() );
		model.addAttribute( "beginIndex", beginPage );
		model.addAttribute( "endIndex", endPage );
		model.addAttribute( "currentIndex", currentPage );
		model.addAttribute( "totalPages", totalPages );
		model.addAttribute( "groups", GroupMapper.map( groupsPage ) );
		order = StringUtils.isEmpty( order ) ? "DESC": order;
		model.addAttribute( "order",  "ASC".equals( order ) ? "DESC": "ASC");
		model.addAttribute( "column", getSortedColumn( column ) );
		model.addAttribute( "searchHelper", new SearchDTO( searchString ) );
	}

	@RequestMapping( value = { "/subjects/{id}" }, method = RequestMethod.GET )
	public String getSubjectsInGroup( @PathVariable( value = "id" ) final Integer id, final ModelMap model ) {
		try {
			Groups group = service.findById( id );
			model.addAttribute( "group", GroupMapper.map( group ));
		} catch ( GroupNotFoundException e ) {
			logger.debug( e );
			model.addAttribute( "status", "groups.not.found" );
			getPage( DEFAULT_PAGE, model, Utils.getSortingDirection( StringUtils.EMPTY ).toString(), StringUtils.EMPTY, StringUtils.EMPTY );
			return "groups/list";
		}
		return "groups/subjects";
	}

	@RequestMapping( value = { "/create" }, method = RequestMethod.GET )
	public String create( final ModelMap model ) {
		model.addAttribute( "groupDTO", new GroupDTO() );
		model.addAttribute( "type", "create" );
		return "groups/edit";
	}

	@RequestMapping( value = { "/create" }, method = RequestMethod.POST )
	public String create( @Valid final GroupDTO groupDTO, BindingResult bindingResult, final ModelMap model ) {
		
		if ( bindingResult.hasErrors() ) {
			model.addAttribute( "type", "create" );
			return "groups/edit";
		}
		
		try {
			service.create( groupDTO );
			model.addAttribute( "status", "groups.create.success" );
		} catch ( final Exception ex ) {
			logger.debug( ex );
			model.addAttribute( "status", "groups.create.failure" );
		}

		getPage( DEFAULT_PAGE, model, Utils.getSortingDirection( StringUtils.EMPTY ).toString(), StringUtils.EMPTY, StringUtils.EMPTY );

		return "groups/list";
	}

	@RequestMapping( value = { "/edit/{id}" }, method = RequestMethod.GET )
	public String edit( @PathVariable( value = "id" ) final Integer id, final ModelMap model ) {

		try {
			final Groups group = service.findById( id );
			model.addAttribute( "groupDTO", GroupMapper.map( group ) );
			model.addAttribute( "type", "edit" );
		} catch ( final GroupNotFoundException ex ) {
			logger.debug( ex );
			model.addAttribute( "status", "groups.not.found" );
			getPage( DEFAULT_PAGE, model, Utils.getSortingDirection( StringUtils.EMPTY ).toString(), StringUtils.EMPTY, StringUtils.EMPTY );
			return "groups/list";
		}

		return "groups/edit";
	}

	@RequestMapping( value = { "/edit" }, method = RequestMethod.POST )
	public String update( @Valid final GroupDTO groupDTO, BindingResult bindingResult, final ModelMap model ) {
		
		if ( bindingResult.hasErrors() ) {
			model.addAttribute( "type", "edit" );
			return "groups/edit";
		}
		
		try {
			service.update( groupDTO );
			model.addAttribute( "status", "groups.update.success" );
		} catch ( final Exception ex ) {
			logger.debug( ex );
			model.addAttribute( "status", "groups.update.failure" );
		}
		getPage( DEFAULT_PAGE, model, Utils.getSortingDirection( StringUtils.EMPTY ).toString(), StringUtils.EMPTY, StringUtils.EMPTY );

		return "groups/list";
	}

	@RequestMapping( value = "/delete/{id}" )
	public String delete( @PathVariable( value = "id" ) final Integer id, final ModelMap model ) {
		try {
			service.delete( id );
			model.addAttribute( "status", "groups.delete.success" );
		} catch ( final GroupNotFoundException ex ) {
			model.addAttribute( "status", "groups.not.found" );
			logger.debug( ex );
		} catch ( final EntityConstraintException ex ) {
			model.addAttribute( "status", "groups.constraint.exception" );
			logger.debug( ex );
		}

		getPage( DEFAULT_PAGE, model, Utils.getSortingDirection( StringUtils.EMPTY ).toString(), GROUP_NAME, StringUtils.EMPTY );
		return "groups/list";
	}

	private Page< Groups > getGroupsPerPage( final String searchString, final PageRequest pageRequest ) {
		Page< Groups > groupsPage = null;

		if ( StringUtils.isEmpty( searchString ) ) {
			groupsPage = service.findAll( pageRequest );
		} else {
			groupsPage = service.findAllMatching( searchString, pageRequest );
		}
		return groupsPage;
	}

	private String getSortedColumn( final String column ) {
		if ( StringUtils.isEmpty( column ) ) {
			return GROUP_NAME;
		} else if ( ( !column.equals( GROUP_NAME ) && !column.equals( NUMERIC_GROUP_NAME ) ) ) {
			return GROUP_NAME;
		}
		return column;
	}
}